﻿using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.ComponentModel;
using System.Reflection;
using System.Globalization;
using System.Resources;
using EnvDTE;
using System.Data;

namespace SPVisualDev.VSAddin.Dialogs.PropertySheets
{
    internal class PropSheetBase : IPropertySheet
    {
        private XmlElement m_xml_element;

        [Browsable(true), DisplayName("Element file name"), RefreshProperties(RefreshProperties.None), TypeConverter(typeof(ElementFileTypeExpandableConverter))]
        public virtual string ElementFileName
        {
            get;
            set;
        }

        [Browsable(false)]
        public SpecialProject Project
        {
            get;
            set;
        }

        [Browsable(false)]
        public ElementInfo ElementInfo
        {
            get;
            set;
        }

        [Browsable(false)]
        public string DefaultResourceFile
        {
            get;
            set;
        }

        [Browsable(false)]
        public ProjectItem ParentFeatureFolder
        {
            get;
            set;
        }

        [Browsable(false)]
        public bool HasUnsavedResourceChanges
        {
            get
            {
                foreach (PropertyInfo pInfo in GetAllManagedProperties())
                {
                    if (pInfo.PropertyType == typeof(ResourceStringContainer))
                    {

                        ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                        if (container.HasUnsavedChanges && !container.IsResourceReadOnly)
                            return true;
                    }
                }


                return false;

            }
            set
            {
                foreach (PropertyInfo pInfo in GetAllManagedProperties())
                {
                    if (pInfo.PropertyType == typeof(ResourceStringContainer))
                    {
                        ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                        container.HasUnsavedChanges = value;
                    }
                }
            }
        }

        [Browsable(false)]
        public virtual XmlElement PropXmlNode
        {
            get
            {
                if (m_xml_element == null)
                    return null;

                List<PropertyInfo> list = GetAllManagedProperties();
                foreach (PropertyInfo pInfo in list)
                {
                    bool excluded = false;
                    if (m_xml_element.GetAttributeNode(pInfo.Name) == null && !this.ElementInfo.New)
                    {
                        excluded = true;
                    }

                    if (pInfo.PropertyType == typeof(ResourceStringContainer))
                    {
                        ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                        string value;
                        if (container.RawString == null)
                            value = string.Empty;
                        else
                            value = container.RawString;

                        if (!excluded || !string.IsNullOrEmpty(value))
                        {
                            Common.SetXmlAttribute(m_xml_element, pInfo.Name, value);
                        }
                    }
                    else
                    {
                        object o = pInfo.GetValue(this, null);
                        if (o != null)
                        {
                            string raw_str = pInfo.GetValue(this, null).ToString();

                            if (!excluded || !string.IsNullOrEmpty(raw_str))
                            {
                                if (pInfo.PropertyType == typeof(bool) || pInfo.PropertyType.Name == "Nullable`1")
                                    Common.SetXmlAttributeIgnoreIfValueMatches(m_xml_element, pInfo.Name, raw_str);
                                else
                                    Common.SetXmlAttribute(m_xml_element, pInfo.Name, raw_str);
                            }
                        }
                    }
                }

                return m_xml_element;
            }
            set
            {
                m_xml_element = value;
                if (m_xml_element == null)
                    return;

                List<PropertyInfo> list = GetAllManagedProperties();
                foreach (PropertyInfo pInfo in list)
                {
                    string raw_str = Common.GetXmlAttribute(m_xml_element, pInfo.Name);

                    if (pInfo.PropertyType.Name == "Nullable`1")
                    {
                        if (!string.IsNullOrEmpty(raw_str))
                            pInfo.SetValue(this, bool.Parse(raw_str), null);
                    }
                    else if (pInfo.PropertyType == typeof(ResourceStringContainer))
                    {
                        ResourceStringContainer container = new ResourceStringContainer(raw_str, this.DefaultResourceFile, this.ParentFeatureFolder);
                        if (pInfo.PropertyType == typeof(ResourceStringContainer))
                        {
                            pInfo.SetValue(this, container, null);
                        }
                    }
                    else
                    {
                        if (pInfo.PropertyType == typeof(string))
                            pInfo.SetValue(this, raw_str, null);
                        else if (pInfo.PropertyType == typeof(int) && !string.IsNullOrEmpty(raw_str))
                            pInfo.SetValue(this, int.Parse(raw_str), null);
                        else if (pInfo.PropertyType == typeof(bool) && !string.IsNullOrEmpty(raw_str))
                            pInfo.SetValue(this, bool.Parse(raw_str), null);

                    }
                }

                this.HasUnsavedResourceChanges = false;
            }
        }

        private List<PropertyInfo> GetAllManagedProperties()
        {
            List<PropertyInfo> list = new List<PropertyInfo>();

            PropertyInfo[] props = this.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (PropertyInfo pInfo in props)
            {
                object[] o = pInfo.GetCustomAttributes(typeof(CategoryAttribute), false);
                if (o.Length > 0)
                {
                    string cat = (o[0] as CategoryAttribute).Category;
                    if (cat == "Properties" ||
                        cat == "Main info")
                    {
                        list.Add(pInfo);
                    }
                }

            }

            return list;
        }

        public override string ToString()
        {
            return base.ToString();
        }

        public void InvalidateResourceChache()
        {
            foreach (PropertyInfo pInfo in GetAllManagedProperties())
            {
                if (pInfo.PropertyType == typeof(ResourceStringContainer))
                {
                    ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                    container.InvalidateCahe();
                }
            }

        }

        public void SetNewDefaultResourceFile(string ResourceFile)
        {
            this.DefaultResourceFile = ResourceFile;

            foreach (PropertyInfo pInfo in GetAllManagedProperties())
            {
                if (pInfo.PropertyType == typeof(ResourceStringContainer))
                {
                    ResourceStringContainer container = pInfo.GetValue(this, null) as ResourceStringContainer;
                    container.DefaultResourceFile = ResourceFile;
                }
            }
        }

        public void SaveChanges()
        {
            List<PropertyInfo> list = GetAllManagedProperties();
            foreach (PropertyInfo pInfo in list)
            {
                if (pInfo.PropertyType != typeof(ResourceStringContainer))
                    continue;

                ResourceStringContainer res_container = pInfo.GetValue(this, null) as ResourceStringContainer;
                string raw_str = res_container.RawString;

                if (!ResourceHelper.IsResourceString(raw_str))
                    continue;

                ResourceKeyInfo key_info = ResourceHelper.ResolveRawStringDefinition(raw_str, DefaultResourceFile, ParentFeatureFolder);

                IDictionary<string, string> dict = res_container.GetAllCultureValues(true);

                    //= Path.GetDirectoryName(key_info.Path);
                //string file = Path.GetFileNameWithoutExtension(key_info.Path);

                foreach (KeyValuePair<string, string> kvp in dict)
                {
                    string path;
                    if (key_info.Path == null)
                    {
                        key_info = ResourceHelper.ResolveRawStringDefinition(raw_str, DefaultResourceFile, ParentFeatureFolder, kvp.Key);
                        if (key_info.Path == null)
                            continue;

                        path = key_info.Path;
                    }
                    else
                    {
                        if (kvp.Key != string.Empty)
                            path = Path.GetDirectoryName(key_info.Path) + "\\" + key_info.ResourceFile + "." + kvp.Key + ".resx";
                        else
                            path = key_info.Path;
                    }


                    ProjectItem proj_item = null;
                    try
                    {
                        proj_item = key_info.ProjectItem.Collection.Item(Path.GetFileName(path));
                    }
                    catch { }

                    if (proj_item != null)
                    {
                        if (!Common.CheckOutItemFromSCC(proj_item))
                            continue;

                        XmlDataDocument xmldoc = new XmlDataDocument();
                        xmldoc.PreserveWhitespace = true;
                        xmldoc.DataSet.ReadXml(path);

                        List<ResXDataNode> resx_list = new List<ResXDataNode>();
                        DataTable table = xmldoc.DataSet.Tables["data"];
                        foreach (DataRow row in table.Rows)
                        {
                            ResXDataNode node = new ResXDataNode(row["name"] as string, row["value"] as string);
                            if (table.Columns.Contains("comments"))
                                node.Comment = row["comments"] as string;

                            resx_list.Add(node);
                        }

                        bool updated = false;
                        using (ResXResourceWriter writer = new ResXResourceWriter(path))
                        {
                            foreach (ResXDataNode node in resx_list)
                            {
                                if (node.Name == key_info.ResourceKey)
                                {
                                    writer.AddResource(key_info.ResourceKey, kvp.Value);
                                    updated = true;
                                }
                                else
                                {
                                    writer.AddResource(node);
                                }
                            }

                            if (!updated)
                            {
                                writer.AddResource(key_info.ResourceKey, kvp.Value);
                            }

                            writer.Generate();
                            writer.Close();
                        }
                    }
                }


            }

            this.HasUnsavedResourceChanges = false;
        }

        public ResourceStringContainer CreateResourceStringContainer(string RawString)
        {
            return new ResourceStringContainer(RawString, this.DefaultResourceFile, this.ParentFeatureFolder);
        }

        public void SetRuntimeAttributeArgument(string Property, Type attribute, string Field, object Value)
        {
            PropertyDescriptor descriptor = TypeDescriptor.GetProperties(this.GetType())[Property];
            Attribute a = descriptor.Attributes[attribute];
            FieldInfo field = a.GetType().GetField(Field, BindingFlags.NonPublic | BindingFlags.Instance);
            field.SetValue(a, Value);
        }

        [Browsable(false)]
        public bool ElementFileNameHasChanged
        {
            get;
            set;
        }

        public class ElementFileTypeExpandableConverter : ExpandableObjectConverter
        {
            public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
            {
                return false;
            }

            public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
            {
                return true;
            }

            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                return true;
            }

            public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
            {
                return true;
            }

            public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
            {
                PropSheetBase obj = context.Instance as PropSheetBase;

                List<string> valid_element_files = new List<string>();

                foreach (ProjectItem item in obj.ParentFeatureFolder.ProjectItems)
                {
                    if (item.Kind != Constants.vsProjectItemKindPhysicalFile)
                        continue;

                    if (Path.GetExtension(item.Name.ToLower()) != ".xml")
                        continue;

                    XmlDocument xmldoc = new XmlDocument();
                    xmldoc.Load(Common.GetProjectItemPath(item));

                    if (xmldoc.DocumentElement.LocalName == "Elements")
                    {
                        valid_element_files.Add(item.Name);
                    }
                }

                return new StandardValuesCollection(valid_element_files);
            }

            public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
            {
                PropSheetBase prop_sheet = context.Instance as PropSheetBase;

                string s = value as string;
                if (!string.IsNullOrEmpty(s))
                {
                    string name = s;
                    if (name.LastIndexOf(".") != -1)
                        name = name.Substring(0, name.LastIndexOf("."));

                    name = Common.FilterIllegalCharsInFileName(name) + ".xml";

                    prop_sheet.ElementFileNameHasChanged = true;

                    return name;

                }
                else
                {
                    return prop_sheet.ElementInfo.ElementFileName;
                }
            }

            public override bool GetPropertiesSupported(ITypeDescriptorContext context)
            {
                return false;
            }
        }
    }

}
